#include "snowboy/snowboy-detect-c-wrapper.h" // 引入Snowboy唤醒词检测库的C语言接口
#include <stdlib.h> // 标准库，提供内存分配、随机数等函数
#include <stdio.h>  // 标准输入输出库
#include "record.h" // 音频录制相关函数
#include "stt.h"    // 语音识别相关函数
#include "config.h" // 配置文件读取相关函数
#include "http.h"   // HTTP请求相关函数
#include "play.h"   // 音频播放相关函数
#include <uuid/uuid.h> // UUID生成库
#include <resolv.h> // DNS解析库

#define KEY2_LINE 7  // 定义按键2连接的GPIO线编号
#define KEY3_LINE 8  // 定义按键3连接的GPIO线编号

// 定义平台的app_id和appid
char* app_id = "8a5c6b47-5e57-43fa-99a2-ec451279d221";
char* appid = "7352428416";

// 获取音频回放通道音量的函数
// card: 声卡名称
// selem: 控制项名称
// 返回值: 当前音量
int get_playback_volume(const char* card, const char* selem) {
    // 声明变量
    int err;
    snd_mixer_t *handle; // 混音器句柄
    snd_mixer_selem_id_t *sid; // 混音器简单元素ID

    // 打开混音器，如果失败打印错误并返回错误码
    if ((err = snd_mixer_open(&handle, 0)) < 0) {
        fprintf(stderr, "Mixer %s open error: %s\n", card, snd_strerror(err));
        return err;
    }

    // 附加控制接口到混音器，如果失败打印错误并清理资源后返回错误码
    if ((err = snd_mixer_attach(handle, card)) < 0) {
        fprintf(stderr, "Mixer attach %s error: %s\n", card, snd_strerror(err));
        snd_mixer_close(handle);
        return err;
    }

    // 注册混音器，如果失败打印错误并清理资源后返回错误码
    if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
        fprintf(stderr, "Mixer register error: %s\n", snd_strerror(err));
        snd_mixer_close(handle);
        return err;
    }

    // 加载混音器元素，如果失败打印错误并清理资源后返回错误码
    if ((err = snd_mixer_load(handle)) < 0) {
        fprintf(stderr, "Mixer %s load error: %s\n", card, snd_strerror(err));
        snd_mixer_close(handle);
        return err;
    }

    // 分配简单元素ID，用于后续操作
    snd_mixer_selem_id_alloca(&sid);

    // 设置简单元素的名称，指向需要操作的控制项
    snd_mixer_selem_id_set_name(sid, selem);

    // 查找简单元素，如果找不到打印错误并清理资源后返回错误码
    snd_mixer_elem_t *elem = snd_mixer_find_selem(handle, sid);
    if (!elem) {
        fprintf(stderr, "Unable to find simple control '%s'\n", selem);
        snd_mixer_close(handle);
        return -ENOENT;
    }

    // 获取回放通道音量，如果失败打印错误并清理资源后返回错误码
    long volume = 0;
    if ((err = snd_mixer_selem_get_playback_volume(elem, 0, &volume)) < 0) {
        fprintf(stderr, "Unable to get playback volume: %s\n", snd_strerror(err));
        snd_mixer_close(handle);
        return err;
    }

    // 关闭混音器
    snd_mixer_close(handle);

    // 成功返回音量值
    return volume;
}

// 调整音量的函数
void adjust_volume(long volume_change) {
    // 声明变量
    long min, max;
    snd_mixer_t *handle;
    snd_mixer_selem_id_t *sid;
    const char *card = "default"; // 使用默认声卡
    const char *selem_name = "PCM"; // 调整PCM音量

    // 打开混音器并加载控制项
    snd_mixer_open(&handle, 0);
    snd_mixer_attach(handle, card);
    snd_mixer_selem_register(handle, NULL, NULL);
    snd_mixer_load(handle);

    // 分配简单元素ID并设置索引和名称
    snd_mixer_selem_id_alloca(&sid);
    snd_mixer_selem_id_set_index(sid, 0);
    snd_mixer_selem_id_set_name(sid, selem_name);
    snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);

    // 获取音量范围
    snd_mixer_selem_get_playback_volume_range(elem, &min, &max);

    // 获取当前音量
    long volume;
    snd_mixer_selem_get_playback_volume(elem, 0, &volume);

    // 调整音量，确保不会超出范围
    volume += volume_change;
    if (volume < min) volume = min;
    if (volume > max) volume = max;

    // 设置调整后的音量
    snd_mixer_selem_set_playback_volume_all(elem, volume);

    // 关闭混音器
    snd_mixer_close(handle);
}

// 创建会话的函数
// authtoken: 授权令牌
// 返回值: 会话ID
char* create_conversation(char* authtoken) {
    // 定义API的URL
    char* url = "https://qianfan.baidubce.com/v2/app/conversation"; 

    // 使用asprintf拼接Authorization字段
    char* auth;
    asprintf(&auth, "Authorization: Bearer %s", authtoken);

    // 初始化请求头部字段链表，并添加必要的字段
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json;charset=utf-8");
    headers = curl_slist_append(headers, auth);

    // 创建JSON对象并添加字段
    cJSON* obj = cJSON_CreateObject();
    cJSON_AddStringToObject(obj, "app_id", app_id);
    // 将JSON对象转换为字符串
    char* json = cJSON_Print(obj);

    // 发送POST请求，并获取响应
    size_t size = strlen(json);
    char* response = http_post(url, headers, json, &size);
    cJSON_Delete(obj);
    free(json);
    free(auth);
    curl_slist_free_all(headers);

    // 如果请求失败，返回NULL
    if (!response) {
        return NULL;
    }

    // 解析响应的JSON数据
    obj = cJSON_ParseWithLength(response, size);
    free(response);

    // 如果解析失败，打印错误并返回NULL
    if (!obj) {
        fprintf(stderr, "解析 JSON 失败: [%s]\n", cJSON_GetErrorPtr());
        return NULL;
    }

    // 从响应中获取conversation_id
    cJSON* conversation_id = cJSON_GetObjectItem(obj, "conversation_id");
    if (!conversation_id) {
        fprintf(stderr, "conversation_id 字段不存在\n");
        cJSON_Delete(obj);
        return NULL;
    }

    // 复制conversation_id并返回
    char* retval = strdup(conversation_id->valuestring);
    cJSON_Delete(obj);

    return retval;
}

// 调用对话API的函数
// authtoken: 授权令牌
// conv_id: 会话ID
// query: 用户的查询
// 返回值: 对话的回答
char* chat(char* authtoken, char* conv_id, char* query) {
    // 定义API的URL
    char* url = "https://qianfan.baidubce.com/v2/app/conversation/runs"; 

    // 使用asprintf拼接Authorization字段
    char* auth;
    asprintf(&auth, "Authorization: Bearer %s", authtoken);

    // 初始化请求头部字段链表，并添加必要的字段
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json;charset=utf-8");
    headers = curl_slist_append(headers, auth);

    // 创建JSON对象并添加字段
    cJSON* obj = cJSON_CreateObject();
    cJSON_AddStringToObject(obj, "app_id", app_id);
    cJSON_AddStringToObject(obj, "conversation_id", conv_id);
    cJSON_AddStringToObject(obj, "query", query);
    cJSON_AddBoolToObject(obj, "stream", false);
    
    // 将JSON对象转换为字符串
    char* json = cJSON_Print(obj);

    // 发送POST请求，并获取响应
    size_t size = strlen(json);
    char* response = http_post(url, headers, json, &size);
    cJSON_Delete(obj);
    free(json);
    curl_slist_free_all(headers);
    free(auth);

    // 如果请求失败，返回NULL
    if (!response) {
        return NULL;
    }

    // 解析响应的JSON数据
    obj = cJSON_ParseWithLength(response, size);
    free(response);

    // 如果解析失败，打印错误并返回NULL
    if (!obj) {
        fprintf(stderr, "解析 JSON 失败: [%s]\n", cJSON_GetErrorPtr());
        return NULL;
    }

    // 从响应中获取answer字段
    cJSON* answer = cJSON_GetObjectItem(obj, "answer");
    if (!answer) {
        fprintf(stderr, "answer 字段不存在\n");
        cJSON_Delete(obj);
        return NULL;
    }

    // 复制answer字段的值并返回
    char* retval = strdup(answer->valuestring);
    cJSON_Delete(obj);

    return retval;
}

// 生成UUID的函数
char* gen_uuid(void) {
    // 静态数组用于存放UUID字符串
    static char uuid_str[37];
    uuid_t uuid;
    // 生成UUID
    uuid_generate(uuid);
    // 将UUID格式化为字符串
    uuid_unparse(uuid, uuid_str);
    return uuid_str;
}

// base64解码的函数
size_t base64_decode(const char* base64, char** decoded) {
    // 计算解码后的数据大小
    size_t decoded_size = (strlen(base64) / 4 + 1) * 3;
    *decoded = (char*)malloc(decoded_size);
    if (!*decoded) {
        return 0;
    }

    // 对base64字符串进行解码
    int size = b64_pton(base64, *decoded, decoded_size);
    if (size < 0) {
        return 0;
    }

    return size;
}

//发送请求
static char* send_request(char* text)
{
    //从配置文件中获取 API 密钥
    cJSON* config = read_config("config.json");
    if (!config) {
        return NULL;
    }

    cJSON* ttstoken = cJSON_GetObjectItem(config, "ttstoken");
    if (!ttstoken) {
        fprintf(stderr, "无法获取ttstoken\n");
        return NULL;
    }

    char* url = "https://openspeech.bytedance.com/api/v1/tts";

    char* auth;
    asprintf(&auth, "Authorization: Bearer;%s", ttstoken->valuestring);

    //添加请求头部字段
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    headers = curl_slist_append(headers, auth);
    free(auth);

    //创建请求体
    cJSON* obj = cJSON_CreateObject();

    cJSON* app = cJSON_CreateObject();
    cJSON_AddStringToObject(app, "appid", appid);
    cJSON_AddStringToObject(app, "token", ttstoken->valuestring);
    cJSON_AddStringToObject(app, "cluster", "volcano_tts");
    cJSON_AddItemToObject(obj, "app", app);

    cJSON* user = cJSON_CreateObject();
    cJSON_AddStringToObject(user, "uid", "conference-assistant");
    cJSON_AddItemToObject(obj, "user", user);

    cJSON* audio = cJSON_CreateObject();
    cJSON_AddStringToObject(audio, "voice_type", "BV700_V2_streaming"); //灿灿2.0
    cJSON_AddItemToObject(obj, "audio", audio);

    cJSON* request = cJSON_CreateObject();
    cJSON_AddStringToObject(request, "reqid", gen_uuid());
    cJSON_AddStringToObject(request, "text", text);
    cJSON_AddStringToObject(request, "operation", "query");
    cJSON_AddItemToObject(obj, "request", request);

    char* json = cJSON_Print(obj);
    size_t size = strlen(json);
    cJSON_Delete(obj);
    //puts(json);

    return http_post(url, headers, json, &size);
}

//处理服务器返回的响应消息
static char* process_response(char* response, size_t* size)
{
    //解析 JSON 响应
    cJSON *obj = cJSON_Parse(response);
    if (!obj) {
        fprintf(stderr, "解析 JSON 失败: [%s]\n", cJSON_GetErrorPtr());
        return NULL;
    }

    // 检查响应码是否为3000,表示成功
    cJSON* code = cJSON_GetObjectItem(obj, "code");
    if (!code)
    {
        fprintf(stderr, "JSON 格式错误: 找不到 'code' 字段\n");
        return NULL;
    }

    // 不是则打印错误信息
    if (code->valueint != 3000)
    {
        cJSON* message = cJSON_GetObjectItem(obj, "message");
        if (message)
        {
            fprintf(stderr, "message: %s\n", message->valuestring);
        }
        return NULL;
    }

    // 获取data字段，即base64编码的音频数据
    cJSON* data = cJSON_GetObjectItem(obj, "data");
    if (!data)
    {
        fprintf(stderr, "JSON 格式错误: 找不到 'data' 字段\n");
        return NULL;
    }

    //对data字段的值进行base64解码
    char* audio = NULL;
    size_t audio_size = base64_decode(data->valuestring, &audio);
    if (audio_size == 0)
    {
        fprintf(stderr, "base64解码失败\n");
        return NULL;
    }

    cJSON_Delete(obj);

    *size = audio_size;
    return audio;
}

//语音合成
void text_to_speech(char* text)
{
    // 将文本分段，以确保不会超过长度限制
    size_t max_len = 1000; // 假设最大长度限制为1000个字符
    size_t text_len = strlen(text);
    size_t offset = 0;

    while (offset < text_len) {
        size_t len = max_len;
        if (offset + len > text_len) {
            len = text_len - offset;
        }
    
        char* segment = strndup(text + offset, len);
        
        //调用语音合成 API
        char* response = send_request(segment);
        if (!response) {
            fprintf(stderr, "调用语音合成 API 失败\n");
            return;
        }

        //puts(response);

        //处理服务器返回的响应消息
        size_t size = 0;
        char* audio = process_response(response, &size);
        if (!audio) {
            return;
        }

        //播放音频
        snd_pcm_t *playback; // PCM设备句柄
        snd_pcm_uframes_t period = 999; // 每个周期的帧数
        char *buffer; // 缓冲区，用于存储从文件中读取的音频数据
        FILE *pcm_file; // 输出PCM文件
        int err; // 用于存储错误码

        // 使用fmemopen创建一个基于内存的文件流，用于读取解码后的音频数据
        pcm_file = fmemopen(audio, size, "rb");
        if (!pcm_file) {
            perror("Error opening output file");
            return;
        }

        // 打开音频播放设备
        playback = play_open("hw:0,0", SND_PCM_FORMAT_S16_LE, 1, 24000, &period);
        if (!playback)
        {
            fclose(pcm_file);
            return;
        }

        printf("period: %d frames\n", period);

        // 分配缓冲区，大小为一个周期的字节数
        buffer = (char *) malloc(snd_pcm_frames_to_bytes(playback, period)); // 分配缓冲区
        if (!buffer) {
            perror("malloc");
            play_close(playback);
            fclose(pcm_file);
            return;
        }

        // 从内存文件流中读取音频数据并播放
        while (1) {
            size_t bytes = fread(buffer, 1, snd_pcm_frames_to_bytes(playback, period), pcm_file);
            if (bytes == 0)
            {
                if (ferror(pcm_file))
                {
                    perror("fread");
                    continue;
                }

                if (feof(pcm_file))
                {
                    break;
                }
            }

            // 将读取的数据写入播放设备
            snd_pcm_sframes_t frames = snd_pcm_writei(playback, buffer, snd_pcm_bytes_to_frames(playback, bytes));
            if (frames < 0)
            {
                fprintf(stderr, "Error from write: %s\n", snd_strerror(frames));
                snd_pcm_recover(playback, frames, 0);
            }
        }

        // 清理资源
        free(buffer); // 释放缓冲区
        fclose(pcm_file); // 关闭文件
        play_close(playback);

        free(response);
        offset += len;
    }
}

int main() 
{
    // 读取配置文件中的平台密钥
    cJSON* config = read_config("config.json");
    if (!config) {
        return EXIT_FAILURE;
    }

    cJSON* authtoken = cJSON_GetObjectItem(config, "authtoken");
    if (!authtoken) {
        fprintf(stderr, "配置文件错误: 找不到 'authtoken' 字段\n");
        cJSON_Delete(config);
        return EXIT_FAILURE;
    }

    // 创建会话
    char* conv_id = create_conversation(authtoken->valuestring);
    if (!conv_id) {
        cJSON_Delete(config);
        return EXIT_FAILURE;
    }

    // 创建snowboy检测器
    SnowboyDetect* detector = SnowboyDetectConstructor("common.res", "model.pmdl");
    if (!detector) {
        cJSON_Delete(config);
        return EXIT_FAILURE;
    }

    // 设置灵敏度
    SnowboyDetectSetSensitivity(detector, "0.5");

    // 获取检测器支持的音频数据参数
    int bits = SnowboyDetectBitsPerSample(detector);
    int channels = SnowboyDetectNumChannels(detector);
    int rate = SnowboyDetectSampleRate(detector);

    printf("采样深度: %d\n", bits);
    printf("声道数量: %d\n", channels);
    printf("采样频率: %d\n", rate);

    // 打开音频采集设备
    snd_pcm_uframes_t period = 999;
    snd_pcm_t* capture = record_open("hw:0,0", SND_PCM_FORMAT_S16_LE, channels, rate, &period);
    if (!capture) {
        SnowboyDetectDestructor(detector);
        cJSON_Delete(config);
        return EXIT_FAILURE;
    }

    // 分配缓冲区，存储采集的音频数据
    char* buffer = malloc(snd_pcm_frames_to_bytes(capture, period)); 
    if (!buffer) {
        perror("malloc");
        record_close(capture);
        SnowboyDetectDestructor(detector);
        cJSON_Delete(config);
        return EXIT_FAILURE;
    }

    int recording = 0;
    int silence = 0;
    FILE* memstream = NULL;
    char* audio = NULL;
    size_t audio_size = 0;

    // struct gpiod_chip *chip;
    // struct gpiod_line *key2_line, *key3_line;
    // int key2_value, key3_value, last_key2_value = 1, last_key3_value = 1;  // 初始状态为高电平

    // // 打开GPIO芯片
    // chip = gpiod_chip_open_by_label("GPIOF");  // 根据您的设备可能需要调整
    // if (!chip) {
    //     perror("打开GPIO芯片失败");
    //     return 1;
    // }

    // // 获取GPIO线
    // key2_line = gpiod_chip_get_line(chip, KEY2_LINE);
    // key3_line = gpiod_chip_get_line(chip, KEY3_LINE);
    // if (!key2_line || !key3_line) {
    //     perror("获取GPIO线失败");
    //     gpiod_chip_close(chip);
    //     return 1;
    // }

    // // 将GPIO线设置为输入模式
    // if (gpiod_line_request_input(key2_line, "key2") < 0 ||
    //     gpiod_line_request_input(key3_line, "key3") < 0) {
    //     perror("请求将GPIO线设置为输入模式失败");
    //     gpiod_chip_close(chip);
    //     return 1;
    // }

    while (1) {

        // key2_value = gpiod_line_get_value(key2_line);
        // key3_value = gpiod_line_get_value(key3_line);

        // // 检测KEY2
        // if (key2_value != last_key2_value && key2_value == 0) {
        //     printf("当前音量为：%d\n", get_playback_volume("default", "PCM"));
        //     adjust_volume(5);  // 增加音量
        // }

        // // 检测KEY3
        // if (key3_value != last_key3_value && key3_value == 0) {
        //     printf("当前音量为：%d\n", get_playback_volume("default", "PCM"));
        //     adjust_volume(-5);  // 减小音量
        // }

        // 从PCM读取数据
        snd_pcm_sframes_t frames = snd_pcm_readi(capture, buffer, period); // 从PCM设备读取数据
        if (frames < 0) {
            fprintf(stderr, "Error from read: %s\n", snd_strerror(frames));
            snd_pcm_recover(capture, frames, 0);
            continue;
        }

        // 运行唤醒词检测
        int status = SnowboyDetectRunDetection(detector, (int16_t*)buffer, snd_pcm_frames_to_bytes(capture, frames) / sizeof(int16_t), 0);
        if (status > 0) {
            printf("检测到唤醒词，开始录音\n");
            recording = 1;
            memstream = open_memstream(&audio, &audio_size);
            if (!memstream) {
                perror("open_memstream");
                continue;
            }
        }

        // 正在录音则处理录音数据
        if (recording) {
            if (status == -2) {
                silence++;
            }

            if (status == 0) {
                silence = 0;
            }

            if (silence > 80) {
                printf("停止录音\n");
                recording = 0;
                silence = 0;
                if (memstream) {
                    fclose(memstream);
                    memstream = NULL;
                }

                if (audio_size) {
                    // 暂停录音
                    snd_pcm_drop(capture);
                    // 识别语音
                    char* text = speech_to_text(audio, audio_size);
                    if (text) {
                        puts(text);
                        // 调用百度云千帆AppBuilder API进行对话
                        char* answer = chat(authtoken->valuestring, conv_id, text);
                        if (answer) {
                            printf("< %s\n", answer);
                            // 将回答转换为语音并播放
                            text_to_speech(answer);
                            free(answer);
                        }
                        free(text);
                    }
                    // 恢复录音
                    snd_pcm_prepare(capture);
                }
            }

            if (memstream) {
                fwrite(buffer, 1, snd_pcm_frames_to_bytes(capture, frames), memstream);
            }
        }
    }

    free(buffer);
    record_close(capture);
    SnowboyDetectDestructor(detector);
    cJSON_Delete(config);
    free(conv_id);

    return EXIT_SUCCESS;
}